﻿//*** Guidance generated code ***//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using ProjectBase.Core;

using $prjsn$.Entity;
using $prjsn$.Common;
using $prjsn$.Common.Validation;
using $prjsn$.Common.Data;

namespace $safeprojectname$
{
    public class UniqueAdditionalPropertyRule<TEntity, TID, TFilter> : RuleBase, IValidationRule<TEntity>
        where TEntity : class, IPersistentObjectWithTypedId<TID>
        where TFilter : BusinessObjectFilter, IFilter<TID>, new()
    {
        #region members

        private string _componentName;
        private IField _field;

        #endregion members

        #region Constructor

        public UniqueAdditionalPropertyRule(string componentName, IField field)
        {
            CheckProperties(componentName);

            PropertyName = field.PropertyName;
            _componentName = componentName;
            _field = field;
        }

        private static void CheckProperties(string propertyName)
        {
            if (typeof(TEntity).GetProperty(propertyName).IsNull())
            {
                throw new ArgumentOutOfRangeException("The property name [{0}] of the entity [{1}] does not exist."
                                                          .FormatWith(propertyName, typeof(TEntity).Name));
            }
            if (typeof(TFilter).GetProperty(propertyName).IsNull())
            {
                throw new ArgumentOutOfRangeException("The parameter [{0}] of the Filter [{1}] does not exist."
                                                          .FormatWith(propertyName, typeof(TFilter).Name));
            }
        }

        #endregion Constructor

        #region Validate

        public virtual ValidationResult Validate(TEntity entity, out ValidationMessage message)
        {
            message = null;

            var dao = ProjectBase.Core.Service.Factory.CreateInstance<IBaseDao<TEntity, TID, TFilter>>();

            var filter = new TFilter
            {
                TakeRows = 1,
            };

            var itemId = entity.IsTransient() ? "new" : entity.IDToDisplay;

            var entityProperty = typeof(TEntity).GetProperty(ComponentName);

            var component = entityProperty.GetValue(entity, null) as IDictionary;
            if (component.IsNull())
            {
                throw new MissingMemberException("Cannot find property {0} of type IDictionary for type {1}"
                    .FormatWith(ComponentName, typeof(TEntity).Name));
            }

            var hasValue = component.Contains(PropertyName);

            // the KEY is missing
            if (!hasValue)
            {
                if (!Field.IsRequired)
                {
                    return ValidationResult.IsValid;
                }
                message = new ValidationMessage(Str.Messages.MissingProperty2)
                {
                    itemId,
                    PropertyName.GetLocalized(typeof(TEntity).Name),
                };
                return ValidationResult.Error;
            }

            var value = component[PropertyName];

            // the Value of the KEY is null
            if (value.IsNull())
            {
                if (!Field.IsRequired)
                {
                    return ValidationResult.IsValid;
                }
                message = new ValidationMessage(Str.Messages.MissingProperty2)
                {
                    itemId,
                    PropertyName.GetLocalized(typeof(TEntity).Name),
                };
                return ValidationResult.Error;
            }

            filter.Additional["Additional." + PropertyName] = value;

            var original = dao.Find(entity.ID, filter).FirstOrDefault();
            if (original.Is())
            {
                message = new ValidationMessage(Str.Messages.ItemAlreadyExists3)
                {
                    original.GetType().Name,
                    original.IDToDisplay,
                    PropertyName,
                };
                return ValidationResult.Error;
            }
            return ValidationResult.IsValid;
        }

        #endregion Validate

        #region properties

        protected virtual string ComponentName
        {
            get { return _componentName; }
            set { _componentName = value; }
        }

        protected virtual IField Field
        {
            get { return _field; }
            set { _field = value; }
        }

        #endregion properties
    }
}